#include "FastNetDevice.hh"

namespace requestDevices
{

FastNetDevice::FastNetDevice(std::string intf) : i(0), intfName(intf), sysReady(false)
{
	  listener = socket(PF_INET, SOCK_STREAM, 0);
  int yes = 1;
  if(listener == -1) {
    std::cerr << "Problem opening socket:" << strerror(errno) << std::endl;
    exit(1);
  }
  if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
    std::cerr << "Problem setting socket options: " << strerror(errno) << std::endl;
    exit(1);
  }
  sockaddr_in my_addr;
  
    my_addr.sin_family = AF_INET;
  my_addr.sin_port = htons(LAGNIAPPE_PORT);
  my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
  
  unsigned int bindReturn = bind(listener, (struct sockaddr *)&my_addr, sizeof(my_addr));
  if(bindReturn != 0) {
    close(listener);
    std::cerr << "Problem with binding" << strerror(errno) << std::endl;
    exit(1);
  }
  if(listen(listener, 500) == -1) {
    std::cerr << "Listening error: " << strerror(errno) << std::endl;
    exit(1);
  }
  FD_SET(listener, &openSet);
  maxFD = listener;
  {
  	Synchronized::Scope mutex(this);
  	sysReady = true;
  	notifyAll();
  }
	
}

FastNetDevice::~FastNetDevice()
{
}

void 
FastNetDevice::run(lagniappe::RCon * rcon)
{
	NetRequest * req = static_cast<NetRequest *>(rcon->getRData());
  	if(send(req->getSocketID(), req->getBuffer(), req->getNumBytes(), 0) == -1) {
    	std::cerr << "Send error: " << strerror(errno) << std::endl;
    	exit(1);
  	}
  	// clean up
  	delete[] req->getBuffer();
  	delete req;
  	delete rcon;
  	return;
}

socket_t
FastNetDevice::getActiveSocket() 
{
	Synchronized::Scope mutex(this);
	while(!sysReady) {
		wait();
	}
	fd_set readSet;
	socket_t ret = -1;
	sockaddr_in remoteAddr;
	socklen_t addrlen;

	// set which sockets to check, and call select
	readSet = openSet;
    if(select(maxFD+1, &readSet, NULL, NULL, NULL) == -1) {
      	if(errno != EINTR) {
			std::cerr << "Select error: " << strerror(errno) << std::endl;
			exit(1);
      	}
    }
    // if current socket set by i is ready, then check it, else just return -1
   	if(FD_ISSET(i, &readSet)) {
		if (i == listener) {
  			// new connection so set up new socket
  			addrlen = sizeof(remoteAddr);
  			socket_t newSock;
  			if((newSock = accept(listener, (struct sockaddr *)&remoteAddr, &addrlen)) == -1) {
    			std::cerr << "Accept error: " << strerror(errno) << std::endl;
    			exit(1);
  			}
  			else {
    			int yes = 1;
    			if (setsockopt(newSock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
      				std::cerr << "Problem setting socket options: " << strerror(errno) << std::endl;
      				exit(1);
    			}
    			FD_SET(newSock, &openSet);
    			if(newSock > maxFD) {
      				maxFD = newSock;
    			}
    
  			}
		}
		else {
			// existing connection, give it to thread to read data
			ret = i;
		}	
   	}
   	// move i along up to it being equal to the max descriptor
   	i++;
   	if(i > maxFD) {
   		i = 0;
   	}
   	std::cerr << "i: " << i << " ret: " << ret << std::endl;
	return ret;
}

void
FastNetDevice::start()
{
	for(int i = 0; i < NUM_THREADS; ++i) {
		threadList.push_back(new FastNetDeviceThread(this));
	}
	for(std::list<FastNetDeviceThread *>::iterator iter = threadList.begin();
		iter != threadList.end(); iter++) {
			(*iter)->setReady();
	}
	
}

}
